using System;
using System.Data;
using System.Reflection;
using System.Configuration;
using System.Security;
using System.Security.Permissions;
using System.Security.Principal;
using System.Xml;
using System.Xml.Serialization;
using System.IO;
using System.Collections.Specialized;
using Microsoft.SharePoint;
using System.Globalization;
using SharePoint.ImportExport.Core;
namespace SharePoint2003Import
{

	/// <summary>
	/// Summary Class for maintaining global data.
	/// </summary>
	public class Importer
	{
		private bool Initialized=false;
		private bool PortalSupport=false; //Default to only WSS installation
		private bool AllowImportToExistingLibraries=false;
		private bool AllowImportToExistingSites=false;
		public bool UpdateExistingListItems=false;
		/// <summary>
		/// Flag to indicate whether the CreateListTemplates oprion was selected
		/// </summary>
		public bool UseTemplates
		{
			get
			{
				return useTemplates;
			}
		}
		private bool useTemplates=false;
		/// <summary>
		/// Locale information, based on the struct.xml file
		/// </summary>
		private static System.IFormatProvider format =null;
		[CLSCompliant(false)] 
			/// <summary>
			/// Locale information, read from the struct.xml file
			/// </summary>
		public static uint lcid=1033;
		/// <summary>
		/// Assembly for managing the SPS functions
		/// </summary>
		public static Assembly PortalAssembly=null;
		/// <summary>
		/// Instance of the SPSWrapper class
		/// </summary>
		public static object PortalAssemblyInstance=null;
		/// <summary>
		/// Method that is used to retrieve the URL of an Area
		/// </summary>
		public static MethodInfo MethodGetAreaUrl;
		/// <summary>
		/// Method that is used to create an Area
		/// </summary>
		public static MethodInfo MethodCreateArea; 
		/// <summary>
		/// Method used to add the created site to the portal search
		/// </summary>
		public static MethodInfo MethodAddToSearch=null;
		/// <summary>
		/// Method to resolve the user from the profiledatabase
		/// </summary>
		public static MethodInfo MethodGetAccountEmailDisplayName=null;
		/// <summary>
		/// Method for adding listings to the specified Area
		/// </summary>
		public static MethodInfo MethodAddListings=null;

		/// <summary>
		/// This controls how often the CG.Collect is called from the document creation loop
		/// </summary>
		public static int GCEvery=-1;


		private string SiteContainer=string.Empty;
		/// <summary>
		/// SiteDirectory
		/// </summary>
		[CLSCompliant(false)] 
		public static SPSite SiteDirectory=null;

		/// <summary>
		/// Helper variable for keeping track of how many docs were uploaded
		/// </summary>
		public static int NumberOfDocs=0;
		/// <summary>
		/// Helper variable for keeping track of how many list items were created
		/// </summary>
		public static int NumberOfListItems=0;
		/// <summary>
		/// Dataset for handling category imports
		/// </summary>
		public static DataSet CategoriesDS=null;

		/// <summary>
		/// This is the number of erraneous items found
		/// </summary>
		public static int NumberOfErrors=0;

		/// <summary>
		/// This is a link to the err_items.xml files
		/// </summary>
		public static string ErrorItems="";

		/// <summary>
		/// Default constructor of the Importer class
		/// </summary>
		public Importer()
		{
			NumberOfDocs=0;
			NumberOfListItems=0;
			try
			{
				string s=ConfigurationSettings.AppSettings.Get("GCEvery");
				int i=int.Parse(s);
				if(i>0)
					GCEvery=i;
			}
			catch
			{
			}

		}
		/// <summary>
		/// Destructor of the Importer class
		/// </summary>
		~Importer()
		{
			if(SiteDirectory!= null)
				SiteDirectory.Close();
		}
		/// <summary>
		/// This member resets tha category dataset
		/// </summary>
		public void CategoryReset()
		{
			if(CategoriesDS != null)
			{
				CategoriesDS.Tables["Import"].Clear();
				CategoriesDS.Tables.Clear();

			}
		}
		/// <summary>
		/// 
		/// </summary>
		public bool Init()
		{
			bool returnValue=true;
			//Check that the user is a Local Admin
			
			WindowsPrincipal myPrincipal = new WindowsPrincipal(WindowsIdentity.GetCurrent());

			if(!myPrincipal.IsInRole(WindowsBuiltInRole.Administrator))
			{
				Logger.Log(ErrorLevel.Error,"The user is not a member of the local Administrators group");
				returnValue=false;
				return returnValue;
			}

			NumberOfDocs=0;
			NumberOfListItems=0;
			if(!Initialized)
			{
				try
				{
					Assembly thisAssembly = Assembly.GetExecutingAssembly();
					string dirName=thisAssembly.Location;
					int pos=dirName.LastIndexOf("\\");
					if(pos>0)
						dirName=dirName.Substring(0,pos);
		
					PortalAssembly = Assembly.LoadFrom(dirName+"\\SPSWrapper.DLL");
					PortalAssemblyInstance = PortalAssembly.CreateInstance("SharePoint2003.Import.PortalWrapper");
					if(null ==PortalAssemblyInstance)
					{
						Logger.Log(ErrorLevel.Information,"Could not create an instance of the class \"SharePoint2003.Import.PortalWrapper\"");
						Initialized=true;
						return returnValue;
							
					}
					
					MethodAddToSearch=PortalAssembly.GetTypes()[0].GetMethod("AddSiteToEnableSearch");

					MethodAddListings=PortalAssembly.GetTypes()[0].GetMethod("AddListings");

					PortalSupport=true;
				}
				catch(FileNotFoundException FileEx)
				{
					Logger.Log(ErrorLevel.Information,"No SPSWrapper.DLL present. This is OK if this machine is a WSS Server. Error: "+FileEx.Message);
				}
				catch(SecurityException secEx)
				{
					Logger.Log(ErrorLevel.Error,"Security error in processing the assembly SPSWrapper.DLL. Error: "+secEx.Message);
				}
				catch(TargetException Ex)
				{
					Logger.Log(ErrorLevel.Error,"Error in processing the assembly SPSWrapper.DLL. Error: "+Ex.Message);
				}
				Initialized=true;
			}
			return returnValue;
		}
		/// <summary>
		/// 
		/// </summary>
		/// <param name="sourceFolder"></param>
		/// <param name="portalRootParam"></param>
		/// <param name="allowImportIntoExistingSite"></param>
		/// <param name="allowImportIntoExistingLibrary"></param>
		/// <param name="importSecurity"></param>
		/// <param name="createListTemplates"></param>
		/// <returns></returns>
		public bool Process(string sourceFolder, string portalRootParam, bool allowImportIntoExistingSite,bool allowImportIntoExistingLibrary, bool importSecurity, bool createListTemplates,bool importDesign,bool updateExistingListItems)
		{
			NumberOfErrors=0;
			useTemplates=createListTemplates;
			portalRoot=portalRootParam;
			bool isPortal=false;
			UpdateExistingListItems=updateExistingListItems;
			
			try

			{

				Logger.Log(ErrorLevel.Progress,"Starting the import...");

				AllowImportToExistingLibraries=allowImportIntoExistingLibrary;
				AllowImportToExistingSites=allowImportIntoExistingSite;

				//Lets get rid of the "must have backslash at the end thingy"...
				string xmlPath = sourceFolder.TrimEnd("\\".ToCharArray()) + "\\" + "struct.xml";

				if(!Miscellaneous.CheckXmlFile(xmlPath,"SharePoint2003Import.XML.struct.xsd"))
					return false;

				// A FileStream is needed to read the XML document.
				FileStream fs = new FileStream(xmlPath, FileMode.Open);
				XmlReader reader = new XmlTextReader(fs);
				// Declare an object variable of the type to be deserialized.
				Import import=new Import();
				// Create an instance of the XmlSerializer specifying type and namespace.
				XmlSerializer serializer = new 	XmlSerializer(typeof(Import));
				
				try
				{	
					// Use the Deserialize method to restore the object's state.
					import = (Import) serializer.Deserialize(reader);
				}
				catch(InvalidOperationException ex)
				{
					Logger.Log(ErrorLevel.Error,"Error loading "+xmlPath+": " + ex.Message+"\t"+ex.InnerException.Message);
					return false;
				}
				finally
				{
					reader.Close();
					fs.Close();
				}

				lcid=UInt16.Parse(import.Settings.Locale,CultureInfo.InvariantCulture);
				format = new System.Globalization.CultureInfo((int)lcid, true);

				Uri spUri=new Uri(portalRoot);
				string thisPortalRoot=spUri.Scheme+"://"+spUri.Authority;
				using(SPSiteWrapper spSite=new SPSiteWrapper(Verbs.modify,importSecurity,thisPortalRoot,AllowImportToExistingLibraries,UseTemplates,null,Guid.Empty))
				{
					SiteContainer=spUri.LocalPath.Substring(1,spUri.LocalPath.Length-1);
					if(!spSite.Open(thisPortalRoot,false))
					{
						Logger.Log(ErrorLevel.Error,"Could not open "+thisPortalRoot+". Last error :\""+Logger.LastError+"\" see log.txt for more information");
						return false;
					}
				
					foreach(RootContainer rootContainer in import.RootContainers)
					{
						SiteContainer=spUri.LocalPath.Substring(1,spUri.LocalPath.Length-1);
						if(SiteContainer.Length==0)
							SiteContainer=rootContainer.Name;

						if(rootContainer.Type==SiteType.Area && !PortalSupport)
						{
							Logger.Log(ErrorLevel.Information,"Error. Trying to import content to a Portal Server Topic Area on a machine that has only WSS installed is prohibited");
							return false;
						}
						if(rootContainer.Type==SiteType.Area)
						{
							#region Portal
							if(!isPortal) //Haven't been here yet so open the Portal
							{
								isPortal=true;
								#region SecurityOverRide
								MethodInfo securitymethod = PortalAssembly.GetTypes()[0].GetMethod("SetSecurity");
								object oSecurityResult=securitymethod.Invoke(PortalAssemblyInstance,null);
								switch(System.Convert.ToInt16(oSecurityResult,CultureInfo.InvariantCulture))
								{
									case 0:// SecurityError.Success:
										Logger.Log(ErrorLevel.Information,"Security registry key applied");
										break;
									case 1: //SecurityError.Unknown//Security could not be set
										Logger.Log(ErrorLevel.Information,"Security registry key could not be applied");
										return false;
									case 2: //SecurityError.AlreadySet: //Security SID was already set
										Logger.Log(ErrorLevel.Information,"Security registry key has already been applied");
										break;
									case 3: //SecurityError.NotLocalAdmin
										Logger.Log(ErrorLevel.Information,"You must be a local administrator to perform this operation");
										return false;
								}



								#endregion
								MethodInfo methodOpenPortal = PortalAssembly.GetTypes()[0].GetMethod("OpenPortal");
								object[] parametersOpenPortal= new object[ 1 ];
								parametersOpenPortal[0]=new Uri(thisPortalRoot);
								object oresOpenPortal=methodOpenPortal.Invoke(PortalAssemblyInstance,parametersOpenPortal);
								if(!Convert.ToBoolean(oresOpenPortal,CultureInfo.InvariantCulture))
								{
									Logger.Log(ErrorLevel.Error,"Could not open "+thisPortalRoot);
									return false;
								}
								Logger.Log(ErrorLevel.Information,thisPortalRoot+" opened succesfully");

							}
							#endregion
							#region Area Import
								
							string portalUrl=thisPortalRoot;
							MethodInfo methodGetSystemAreaGuid = PortalAssembly.GetTypes()[0].GetMethod("GetSystemAreaGuid");
							MethodGetAreaUrl = PortalAssembly.GetTypes()[0].GetMethod("GetAreaUrl");
							object[] parameters= new object[ 1 ];
							parameters[0]=SiteContainer;
							object ores=methodGetSystemAreaGuid.Invoke(PortalAssemblyInstance,parameters);
							string areaGuid=Convert.ToString(ores,Format);
							if(areaGuid.Length!=0)
							{
								Guid rootID=new Guid(areaGuid);
								MethodCreateArea = PortalAssembly.GetTypes()[0].GetMethod("Create");
								//Enumerate through all Areas
								foreach(Container container in rootContainer.Containers)

								{
									string areaName = container.Name;
									parameters= new object[ 5 ];
									parameters[0]=rootID;
									parameters[1]=new Uri(portalUrl);
									parameters[2]=container;
									parameters[3]=importSecurity;
									parameters[4]=AllowImportToExistingSites;
									ores=MethodCreateArea.Invoke(PortalAssemblyInstance,parameters);
			
									Guid areaID=new Guid(Convert.ToString(ores,Format));
					
									if(areaID==Guid.Empty) 
									{
										Logger.Log(ErrorLevel.Error,"Could not create the area: "+areaName);
										continue;
									}
									parameters= new object[ 1 ];
									parameters[0]=areaID;
									ores=MethodGetAreaUrl.Invoke(PortalAssemblyInstance,parameters);
									string areaURL=System.Convert.ToString(ores,Format);
									if(areaURL.Length>0)
									{
										using(SPSiteWrapper portalArea = new SPSiteWrapper(Verbs.modify,importSecurity,portalRoot,AllowImportToExistingLibraries,UseTemplates,null,Guid.Empty))
										{
											portalArea .ID=areaID;
											portalArea .isSite=false;
							
											if(portalArea .Open(thisPortalRoot+areaURL,false))
											{
												if (createListTemplates)
												{
													/*Create templates AFTER the top-level area is created
														* to ensure that templates will be available -- create them in the physical
														* area instead of a temporary area -- also faster. */
													string sListXMLPath = sourceFolder.TrimEnd("\\".ToCharArray()) + "\\" + "listtemplates.xml";

													if(!portalArea .CreateListTemplates(sListXMLPath))
													{
														Logger.Log(ErrorLevel.Error,"Could not create the List Templates"+System.Environment.NewLine + "Last error: \""+Logger.LastError+"\" see log.txt for more details");
														continue;
													}
										
												}
												//Enumerate through all datacontent in the Area
												foreach(DataContainer dataContainer in container.DataContainers)
												{
													try
													{
														if(dataContainer.Template=="Listings")
														{

															if(null != Importer.MethodAddListings)
															{
																object[] listingsparameters= new object[ 4 ];
																listingsparameters[0]=Importer.PortalRoot;
																listingsparameters[1]=areaID;
																listingsparameters[2]=dataContainer;
																listingsparameters[3]=sourceFolder;
																object olistingsres=Importer.MethodAddListings.Invoke(Importer.PortalAssemblyInstance,listingsparameters);

															}

										
														}
														else
															portalArea.MigrateContentData(dataContainer, sourceFolder);
													}
													catch(Exception ContentEx)
													{
														Logger.Log(ErrorLevel.Error,"Exception occurred when migrating data to "+areaName+". Error: "+ContentEx.Message);
													}
												}
												portalArea.MigrateDesign(container.Design,sourceFolder,importDesign);

												//Enumerate through all Areas

												foreach(Container subAreaContainer in container.Containers)
												{
													try
													{
														portalArea.MigrateContent(subAreaContainer, sourceFolder);
													}
													catch(System.Reflection.TargetInvocationException ContentEx)
													{
														Logger.Log(ErrorLevel.Error,"Exception occurred when migrating content to "+areaName+". Error: "+ContentEx.Message);
													}
												}
												if (createListTemplates)
												{
													string sListXMLPath = sourceFolder.TrimEnd("\\".ToCharArray()) + "\\" + "listtemplates.xml";
													Logger.Log(ErrorLevel.Information,"Removing the temporary Templates...");
													portalArea.DeleteListTemplates(sListXMLPath);
												}

											}
										}
									}
									else
									{
										Logger.Log(ErrorLevel.Error,"Could not open the area: "+areaName);
										continue;
									}
								}
							}

							
							#endregion
						}
						if(rootContainer.Type==SiteType.Site)
						{
							#region Site Imports
							foreach(Container container in rootContainer.Containers)
							{
								using(SPSiteWrapper spWSSSite = new SPSiteWrapper(container.Operation,importSecurity,thisPortalRoot,AllowImportToExistingLibraries,UseTemplates,null,Guid.Empty))
								{
									string sitename = Miscallaneous.FixName(container.Name);
									bool created=true;
									if(!spWSSSite.Create(SiteContainer,container,AllowImportToExistingSites,importSecurity,out created ))
									{
										Logger.Log(ErrorLevel.Error,"Could not create the site "+sitename+ " last logger error : \""+Logger.LastError+"\" see log.txt for more details");
										continue;
									}
									string thisUrl=spWSSSite.Url;

									if(created)					
										Logger.Log(ErrorLevel.Progress,"Site "+sitename+" created");
									else
										Logger.Log(ErrorLevel.Progress,"Site "+sitename +"( "+spWSSSite.Web.Url+" ) opened");
									spWSSSite.DesignMigrated=importDesign;
									spWSSSite.SiteCreated=created;
									if (createListTemplates)
									{
										string sListXMLPath = sourceFolder.TrimEnd("\\".ToCharArray()) + "\\" + "listtemplates.xml";
										Logger.Log(ErrorLevel.Information,"Creating temporary templates...");
										spWSSSite.CreateListTemplates(sListXMLPath);
									}

									foreach(DataContainer dataContainer in container.DataContainers)
									{
										//Migrate the content to the site
										//This might include the creation of list or libraries
										if(!spWSSSite.MigrateContentData(dataContainer, sourceFolder))
										{
											Logger.Log(ErrorLevel.Error,"Last error: \""+Logger.LastError+"\" see log for more details");
											continue;
										}
									}
									foreach(DataContainer dataContainer in container.DataContainers)
									{
										if(dataContainer.DocumentTemplateUrl.Length>0)
										{
											SPDocumentLibrary lib=(SPDocumentLibrary)spWSSSite.Web.Lists[dataContainer.Name];
											if(lib.DocumentTemplateUrl!=spWSSSite.Web.ServerRelativeUrl+"/"+dataContainer.DocumentTemplateUrl)
											{
												lib.DocumentTemplateUrl=spWSSSite.Web.ServerRelativeUrl+"/"+dataContainer.DocumentTemplateUrl;
												lib.Update();
											}


										}
									}

									
									foreach(Container subWebContainer in container.Containers)
									{
										//Migrate the content to the site
										//This might include the creation of subsites
										if(!spWSSSite.MigrateContent(subWebContainer, sourceFolder))
										{
											Logger.Log(ErrorLevel.Error,"Last error: \""+Logger.LastError+"\" see log for more details");
											continue;
										}
										spWSSSite.Open(thisUrl,true);
									}
									try
									{
										spWSSSite.MigrateDesign(container.Design,sourceFolder,importDesign);
									}
									catch(SharePoint2003Import.SpsImportException spEx)
									{
										Logger.Log(ErrorLevel.Error,"Exception when migrating the design. Error: "+spEx.Message );
									}
									if (createListTemplates)
									{
										string sListXMLPath = sourceFolder.TrimEnd("\\".ToCharArray()) + "\\" + "listtemplates.xml";
										Logger.Log(ErrorLevel.Information,"Removing the temporary Templates...");
										spWSSSite.DeleteListTemplates(sListXMLPath);
									}

								}
							}
							#endregion
						}
						

					}
				}


					
				Logger.Log(ErrorLevel.Progress,"Added "+NumberOfDocs.ToString(Format)+ " documents");
				Logger.Log(ErrorLevel.Progress,"Added "+NumberOfListItems.ToString(Format)+ " list items");
				if(NumberOfErrors>0)
				{
					Logger.Log(ErrorLevel.Progress,NumberOfErrors.ToString()+ " of erroneous items found ");
					Logger.Log(ErrorLevel.Progress,"The erroneous items can be found in the following files:"+ErrorItems);
				}
				//Process categories
				if(CategoriesDS != null)
				{
					MethodInfo methodCategoryListing = PortalAssembly.GetTypes()[0].GetMethod("AddCategoryListings");
					object[] categoryParameters= new object[ 2 ];
					categoryParameters[0]=portalRoot;

					categoryParameters[1]=CategoriesDS;
					object ocategoryres=methodCategoryListing .Invoke(PortalAssemblyInstance,categoryParameters);
					
				}
						
				Logger.Log(ErrorLevel.Progress,"All done!"+System.Environment.NewLine+"See "+Logger.FileName+ " for more info");
				
			}
			catch(System.Reflection.TargetInvocationException targetEx)
			{
				Logger.Log(ErrorLevel.Error,"Critical error! "+targetEx.InnerException.Message+". Halting import, check log file for details.");
				Logger.Log(ErrorLevel.File,"Exception type "+targetEx.InnerException.GetType().ToString());
				Logger.Log(ErrorLevel.File,targetEx.StackTrace.ToString());
			}

			catch(SPException SpEx)
			{
				Logger.Log(ErrorLevel.Error,"Critical error! "+SpEx.Message+". Halting import, check log file for details.");
				Logger.Log(ErrorLevel.File,"Exception type "+SpEx.InnerException.GetType().ToString());
				Logger.Log(ErrorLevel.File,SpEx.StackTrace.ToString());
			}
			catch(Exception Ex)
			{
				Logger.Log(ErrorLevel.Error,"Critical error! "+Ex.Message+". Halting import, check log file for details.");
				Logger.Log(ErrorLevel.File,"Exception type "+Ex.GetType().ToString());
				Logger.Log(ErrorLevel.File,Ex.StackTrace.ToString());
			}
			finally
			{
				CategoryReset();
				if(null != SiteDirectory)
				{
					SiteDirectory.Close();
					SiteDirectory.Dispose();
				}
				if(isPortal)
				{

					MethodInfo mymethod = PortalAssembly.GetTypes()[0].GetMethod("ClosePortal");
					object[] parameters= null;
					mymethod.Invoke(PortalAssemblyInstance,parameters);

				}
			}
			GC.Collect();
			return true;
		}
		/// <summary>
		/// This is the member that validates the XML files
		/// </summary>
		/// <param name="sourceFolder">This is the start folder</param>
		/// <returns></returns>
		public bool ValidateFiles(string sourceFolder, bool validateListTemplates)
		{
			bool retValue=true;
			string xmlPath = sourceFolder.TrimEnd("\\".ToCharArray()) + "\\" + "struct.xml";
			Logger.Log(ErrorLevel.Progress,"Validating file "+xmlPath);
			Miscellaneous.CheckXmlFile(xmlPath,"SharePoint2003Import.XML.struct.xsd");
			if(validateListTemplates)
			{
				xmlPath = sourceFolder.TrimEnd("\\".ToCharArray()) + "\\" + "listtemplates.xml";
				Logger.Log(ErrorLevel.Progress,"Validating file "+xmlPath);
				Miscellaneous.CheckXmlFile(xmlPath,"SharePoint2003Import.XML.listtemplates.xsd");
				xmlPath = sourceFolder.TrimEnd("\\".ToCharArray()) + "\\" + "struct.xml";	
			}
			XmlDocument doc=new XmlDocument();
			doc.Load(xmlPath);
			foreach(XmlNode itemsNode in doc.SelectNodes("//relativeitemXML"))
			{
				xmlPath = sourceFolder.TrimEnd("\\".ToCharArray()) + "\\" + itemsNode.InnerText;
				Logger.Log(ErrorLevel.Progress,"Validating file "+xmlPath);
				Miscellaneous.CheckXmlFile(xmlPath,"SharePoint2003Import.XML.items.xsd");
			}
			Logger.Log(ErrorLevel.Information,"Validation complete");
			return retValue;

		}

		/// <summary>
		/// This is the format info specified by the struct.xml file
		/// </summary>
		public static IFormatProvider Format
		{
			get
			{
				return format;
			}
		}

		private static string portalRoot;

		/// <summary>
		/// This is the PortalRoot
		/// </summary>
		public static string PortalRoot
		{
			get
			{
				return portalRoot;
			}
		}

		private static bool migrateDesign=false;

		/// <summary>
		/// Boolean flag indicating if design elements are migrated
		/// </summary>
		public static bool MigrateDesign
		{
			get
			{
				return migrateDesign;
			}
			set
			{
			}
		}
	}
}
